/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package com.ceair.flight.refund.util;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.ceair.flight.refund.location.dt.dto.SegPrice;
import com.ceair.flight.refund.location.dt.dto.TktPrice;
import com.ceair.flight.refund.location.dt.enums.OrderScopeEnum;
import com.ceair.flight.refund.location.dt.enums.OrderTypeEnum;
import com.ceair.flight.refund.location.model.FlightOrderDO;
import com.ceair.flight.refund.location.model.PaxDO;
import com.ceair.flight.refund.location.model.SegmentDO;
import com.ceair.flight.refund.location.model.TaxFeeDO;
import com.ceair.flight.refund.location.model.TicketDO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 此工具类不是线程安全类
 *
 * @author youguilin
 */
public class FlightOrderUtil {

    private static final Logger LOG = LoggerFactory.getLogger(FlightOrderUtil.class);
    private final FlightOrderDO flightOrderDO;

    public FlightOrderUtil(FlightOrderDO flightOrder) {
        if (flightOrder == null) {
            throw new IllegalArgumentException("Big order entity is null");
        }
        flightOrderDO = flightOrder;
    }

    public FlightOrderDO getEntity() {
        return flightOrderDO;
    }

    /**
     * 获取改期后, 原始乘客信息
     *
     * @param orignalFlightOrder
     * @param newPax
     * @return
     */
    public static PaxDO getOriginalPax(FlightOrderDO orignalFlightOrder, PaxDO newPax) {
        String newFullName = newPax.getPaxFullName();
        Set<PaxDO> paxs = orignalFlightOrder.getPaxs();
        for (PaxDO pax : paxs) {
            String fullName = pax.getPaxFullName();
            if (!newFullName.isEmpty() && newFullName.equals(fullName)) {
                return pax;
            }
        }
        LOG.error("Could not find original passenger with reschedule pax:{} in orignial flight order: {}", newPax, orignalFlightOrder);
        throw new IllegalStateException("Could not found original pax with reschedule pax id " + newPax.getPassengerId() + " in original flight order No " + orignalFlightOrder.getFlightOrderNo());
    }

    /**
     * 获取订单乘客的PNR号码, 一个机票订单中只有一个PNR
     *
     * @return
     */
    public String getPnr() {
        PaxDO pax = getEntity().getPaxs().iterator().next();
        Set<SegmentDO> segments = pax.getSegments();
        return segments.iterator().next().getPnrCd();
    }

    /**
     * 通过票号找到其乘客数据库ID
     *
     * @param ticketNo
     * @return
     */
    public PaxDO getPaxByTicketNo(String ticketNo) {
        FlightOrderDO flightOrder = getEntity();
        Set<PaxDO> paxes = flightOrder.getPaxs();
        for (PaxDO pax : paxes) {
            if (pax.getTktNo().equals(ticketNo)) {
                return pax;
            }
        }
        throw new IllegalStateException("NO PASSENGER FOUND WITH TICKET " + ticketNo);
    }

    /**
     * 通过票号找到其乘客数据库ID
     *
     * @param ticketNo
     * @return
     */
    public Long getPaxIdByTicketNo(String ticketNo) {
        return getPaxByTicketNo(ticketNo).getPassengerId();
    }

    /**
     * 获取税总额
     *
     * @param taxs
     * @return
     */
    public BigDecimal getTaxAmount(Set<TaxFeeDO> taxs) {
        BigDecimal amount = BigDecimal.ZERO;
        if (taxs == null || taxs.isEmpty()) {
            return amount;
        }
        for (TaxFeeDO tax : taxs) {
            LOG.debug("Tax {}:{}", tax.getTaxTp(), tax.getTaxAm());
            amount = amount.add(tax.getTaxAm());
        }
        LOG.debug("Tax amount:{}", amount);
        return amount;
    }

    /**
     * 获取多航段税总额
     *
     * @param segments
     * @return
     */
    public BigDecimal getTaxAmountOnSeg(Set<SegmentDO> segments) {
        BigDecimal amount = BigDecimal.ZERO;
        if (segments == null || segments.isEmpty()) {
            return amount;
        }
        for (SegmentDO segment : segments) {
            BigDecimal segAmount = getSingleSegTaxAmount(segment);
            amount = amount.add(segAmount);
        }
        LOG.debug("Segment total tax amount:{}", amount);
        return amount;
    }

    /**
     * 获取单航段税总额
     *
     * @param segment
     * @return
     */
    public BigDecimal getSingleSegTaxAmount(SegmentDO segment) {
        Set<TaxFeeDO> taxs = segment.getTaxFees();
        BigDecimal segAmount = getTaxAmount(taxs);
        String cityPair = segment.getDepAirpCd() + segment.getArrAirpCd();
        LOG.debug("Segment:{} amount:{}", cityPair, segAmount);
        return segAmount;
    }

    /**
     * 获取乘客,客票价格信息
     *
     * @return
     */
    public Map<Long, TktPrice> getTktPriceMap() {
        FlightOrderDO flightOrder = getEntity();
        Set<PaxDO> paxs = flightOrder.getPaxs();
        Map<Long, TktPrice> paxTktMap = new HashMap<>();
        for (PaxDO pax : paxs) {
            TktPrice tktPrice = new TktPrice(pax.getFcny(), pax.getScny(), pax.getTaxAm());
            BigDecimal pointsAmt = pax.getPointAmount();
            if (pointsAmt != null) {
                tktPrice.setTktPoints(pointsAmt.longValue());
            }
            LOG.info("Passenger {} ticket price is: {}", pax.getPassengerId(), tktPrice);
            paxTktMap.put(pax.getPassengerId(), tktPrice);
        }
        return paxTktMap;
    }

    /**
     * 获取票相关信息
     *
     * @param ticketNo
     * @return
     */
    public TktPrice getTktPriceByTicket(String ticketNo) {
        Long paxId = getPaxIdByTicketNo(ticketNo);
        Map<Long, TktPrice> tktPriceMap = getTktPriceMap();
        LOG.info("Passenger {}'s ticket {} ticket price is: {}", paxId, ticketNo, tktPriceMap.get(paxId));
        return tktPriceMap.get(paxId);
    }

    /**
     * 获取机票可退金额
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public TktPrice getRefundTktPrice(String ticketNo, int index) {
        TktPrice tktPrice = getTktPriceByTicket(ticketNo);
        if (index <= 1) {
            return tktPrice;
        }
        //如果是国际航线票, 且有已经使用的航段,票面价格无法计算
        String orderScope = flightOrderDO.getOrderScope();
        if (orderScope != null && OrderScopeEnum.INTERNATIONAL == OrderScopeEnum.getByCode(orderScope)) {
            LOG.warn("Used international ticket {} from segment index {} could not calculate ticket price", ticketNo, index);
            return null;
        }
        Set<SegPrice> segPrices = getUsedSegPrice(ticketNo, index);
        return getRefundTktPrice(tktPrice, segPrices);
    }

    /**
     * 获取机票可退金额
     *
     * @param tktPrice
     * @param usedSegsPrices
     * @return
     */
    public TktPrice getRefundTktPrice(TktPrice tktPrice, Set<SegPrice> usedSegsPrices) {
        BigDecimal totalTktPrice = tktPrice.getTktPrice();
        BigDecimal totalTktSalePrice = tktPrice.getTktSalePrice();
        BigDecimal totalTktTaxPrice = tktPrice.getTaxAmount();
        long totalTktPoints = tktPrice.getTktPoints();
        for (SegPrice segPriceEntity : usedSegsPrices) {
            BigDecimal segPrice = segPriceEntity.getSegPrice();
            BigDecimal segSalePrice = segPriceEntity.getSegSalePrice();
            BigDecimal segTaxPrice = segPriceEntity.getTaxAmount();
            totalTktPrice = totalTktPrice.subtract(segPrice);
            totalTktSalePrice = totalTktSalePrice.subtract(segSalePrice);
            totalTktTaxPrice = totalTktTaxPrice.subtract(segTaxPrice);
            totalTktPoints -= segPriceEntity.getSegPoints();
        }
        LOG.debug("Ticket total refundable amount:{}, sale amount:{}, tax amount:{}, points:{}", totalTktPrice, totalTktSalePrice, totalTktTaxPrice, totalTktPoints);
        return new TktPrice(totalTktPrice, totalTktSalePrice, totalTktTaxPrice, totalTktPoints);
    }

    /**
     * 获取票已使用的航段(没有选择退的航段)
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public Set<SegmentDO> getUsedSegs(String ticketNo, int index) {
        Long paxId = getPaxIdByTicketNo(ticketNo);
        Set<SegmentDO> segments = getPaxSegs(paxId);
        Set<SegmentDO> usedSegments = new HashSet<>();
        for (SegmentDO segment : segments) {
            if (segment.getSegSq() < index) {
                usedSegments.add(segment);
            }
        }
        return usedSegments;
    }

    /**
     * 获取所有已使用航段的价格(没有申请退票上的所有航段价格)
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public Set<SegPrice> getUsedSegPrice(String ticketNo, int index) {
        Set<SegmentDO> segments = getUsedSegs(ticketNo, index);
        Set<SegPrice> segPrices = new HashSet<>(segments.size());
        for (SegmentDO seg : segments) {
            SegPrice segPrice = new SegPrice(seg);
            segPrices.add(segPrice);
        }
        return segPrices;
    }

    /**
     * 获取票未使用的航段(所有申请退票的航段)
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public Set<SegmentDO> getUnusedSegs(String ticketNo, int index) {
        Long paxId = getPaxIdByTicketNo(ticketNo);
        Set<SegmentDO> segments = getPaxSegs(paxId);
        Set<SegmentDO> unusedSegments = new HashSet<>(5);
        for (SegmentDO segment : segments) {
            if (segment.getSegSq() >= index) {
                unusedSegments.add(segment);
            }
        }
        return unusedSegments;
    }

    /**
     * 获取票的指定序列的航段信息.
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public SegmentDO getFirstRefundSeg(String ticketNo, int index) {
        Set<SegmentDO> unusedSegs = getUnusedSegs(ticketNo, index);
        for (SegmentDO segment : unusedSegs) {
            if (segment.getSegSq() == index) {
                return segment;
            }
        }
        throw new IllegalStateException("SEGMENT NOT FOUND FOR TICKET: " + ticketNo + " FROM INDEX: " + index);
    }

    /**
     * 获取退票机票中第一个航段的起飞机场
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public String getFirstRefundAirport(String ticketNo, int index) {
        SegmentDO segment = getFirstRefundSeg(ticketNo, index);
        return segment.getDepAirpCd();
    }

    /**
     * 获取乘客所有航段
     *
     * @param paxId
     * @return
     */
    public Set<SegmentDO> getPaxSegs(Long paxId) {
        FlightOrderDO flightOrder = getEntity();
        Set<PaxDO> paxes = flightOrder.getPaxs();
        for (PaxDO pax : paxes) {
            if (Objects.equals(pax.getPassengerId(), paxId)) {
                return pax.getSegments();
            }
        }
        throw new IllegalStateException("NO SEGMENTS FOUND FOR PASSEGNER " + paxId);
    }

    /**
     * 获取所有未使用的税
     *
     * @param ticketNo
     * @param index
     * @return
     */
    public BigDecimal getUnusedTaxPrice(String ticketNo, int index) {
        Set<SegmentDO> unusedSegs = getUnusedSegs(ticketNo, index);
//        BigDecimal unusedTaxAmount_1 = getTaxAmountOnSeg(unusedSegs);
        BigDecimal unusedTaxAmount_2 = BigDecimal.ZERO;
        for (SegmentDO segment : unusedSegs) {
            BigDecimal segTax = segment.getTaxAm();
            if (segTax == null) {
                continue;
            }
            unusedTaxAmount_2 = unusedTaxAmount_2.add(segTax);
        }
        LOG.debug("Taxs on segment level amount:{}", unusedTaxAmount_2);
        return unusedTaxAmount_2;
    }

    /**
     * 获取特定未使用的税
     *
     * @param ticketNo
     * @param index
     * @param taxCode
     * @return
     */
    public BigDecimal getUnusedTaxPrice(String ticketNo, int index, String... taxCode) {
        Set<SegmentDO> unusedSegs = getUnusedSegs(ticketNo, index);
        BigDecimal unusedTaxAmount_2 = BigDecimal.ZERO;
        for (SegmentDO segment : unusedSegs) {
            BigDecimal segTax = segment.getTaxAm();
            if (segTax == null || segTax.compareTo(BigDecimal.ZERO) == 0) {
                continue;
            }
            Set<TaxFeeDO> allTaxs = segment.getTaxFees();
            for (TaxFeeDO tax : allTaxs) {
                if (Arrays.binarySearch(taxCode, tax.getTaxTp()) >= 0) {
                    unusedTaxAmount_2 = unusedTaxAmount_2.add(tax.getTaxAm());
                }
            }
        }
        LOG.debug("Taxs {} on tax segment amount:{}", taxCode, unusedTaxAmount_2);
        return unusedTaxAmount_2;
    }

    public Set<TicketDO> getTickets(String ticketNo) {
        LOG.debug("Getting ticket entity for ticket No: {}", ticketNo);
        PaxDO pax = getPaxByTicketNo(ticketNo);
        return pax.getTickets();
    }

    /**
     * 获取所有客票退票对应的乘客
     *
     * @param ticketNos
     * @return
     */
    public Set<PaxDO> getPax(Collection<String> ticketNos) {
        Set<PaxDO> paxs = new LinkedHashSet<>();
        for (String ticketNo : ticketNos) {
            paxs.add(getPaxByTicketNo(ticketNo));
        }
        return paxs;
    }

    public Set<String> getAllTickets() {
        Set<PaxDO> paxs = flightOrderDO.getPaxs();
        Set<String> allTickets = new HashSet<>(paxs.size(), 1.0f);
        for (PaxDO pax : paxs) {
            allTickets.add(pax.getTktNo());
        }
        return allTickets;
    }

    /**
     * 判断机票订单是否为改期订单, 改期订单中只有一个乘客和对应的一张客票
     *
     * @return
     */
    public boolean isRescheduleFlightOrder() {
        if (flightOrderDO == null) {
            return false;
        }
        Integer orderTypeCode = flightOrderDO.getOrderTp();
        OrderTypeEnum orderType = OrderTypeEnum.getByCode(orderTypeCode);

        String oldOrderId = flightOrderDO.getOldRecId();
        return OrderTypeEnum.RESCHEDULE_ORDER == orderType && oldOrderId != null && !oldOrderId.isEmpty();
    }
}
